package com.csap.framework.apidoc.service;

import com.csap.framework.annotation.apidoc.Api;
import com.csap.framework.annotation.apidoc.ApiModel;
import com.csap.framework.annotation.apidoc.ApiModelProperty;
import com.csap.framework.annotation.apidoc.ApiOperation;
import com.csap.framework.annotation.apidoc.ApiProperty;
import com.csap.framework.annotation.apidoc.ApiResponseCode;
import com.csap.framework.annotation.apidoc.Group;
import com.csap.framework.apidoc.CsapResourcesProvider;
import com.csap.framework.apidoc.DevtoolsClass;
import com.csap.framework.apidoc.config.ScannerPackageConfig;
import com.csap.framework.apidoc.model.CsapDocEnum;
import com.csap.framework.apidoc.model.CsapDocMethod;
import com.csap.framework.apidoc.model.CsapDocMethodHeaders;
import com.csap.framework.apidoc.model.CsapDocModel;
import com.csap.framework.apidoc.model.CsapDocModelController;
import com.csap.framework.apidoc.model.CsapDocParameter;
import com.csap.framework.apidoc.model.CsapDocResponse;
import com.csap.framework.extension.BaseResultCode;
import com.csap.framework.properties.Protocols;
import com.csap.framework.properties.apidoc.ApiStatus;
import com.csap.framework.properties.apidoc.ModelType;
import com.csap.framework.properties.apidoc.ModelTypepProperties;
import com.csap.framework.properties.apidoc.ParamType;
import com.csap.framework.util.ClazzUtils;
import com.csap.framework.util.CollectionsUtil;
import com.csap.framework.util.FinalConfigParam;
import com.csap.framework.util.StringUtil;
import com.csap.framework.util.TypeVariableModel;
import com.csap.framework.util.bean.BeanCopierUtil;
import com.csap.framework.validation.factory.BaseModel;
import com.csap.framework.validation.factory.Validate;
import com.csap.framework.validation.factory.ValidateFactory;
import com.csap.mybatisplus.core.toolkit.StringPool;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 文档处理类
 *
 * @author yangchengfu
 * @date 2019/12/28 10:20 上午
 **/
public final class CsapDocService extends AbstractCsapDocService implements FilterData {


    public CsapDocService(CsapResourcesProvider resourcesProvider) {
        this.resourcesProvider = resourcesProvider;
    }

    public CsapDocService(ScannerPackageConfig scannerPackageConfig) {
        this.packageConfig = scannerPackageConfig;
    }

    public CsapDocService(CsapResourcesProvider resourcesProvider, ScannerPackageConfig scannerPackageConfig, ValidateFactory validateFactory, DevtoolsClass devtoolsClass) {
        this.resourcesProvider = resourcesProvider;
        this.packageConfig = scannerPackageConfig;
        this.validateFactory = validateFactory;
        this.devtoolsClass = devtoolsClass;
    }

    public CsapDocService globalApiList(List<CsapDocModelController> list) {
        globalApiList.addAll(list);
        return this;
    }

    public CsapDocService globalRequestParam(Map<String, List<CsapDocModel>> map) {
        globalRequestParam.putAll(map);
        return this;
    }

    /**
     * 统一set处理
     *
     * @param group 处理的数据
     * @return 返回唯一的数据
     */
    public Set<String> set(String[] group, Set<String> set) {
        Set<String> set1 = new HashSet<>(Arrays.asList(group));
        if (this.group.containsAll(set1) && CollectionsUtil.isNotEmpty(set)) {
            set1 = set;
        }
        return set1;
    }

    public Set<String> set(String[] group) {
        return set(group, null);
    }

    /**
     * 处理apdoc包路径
     *
     * @param tableName tablename
     * @return apidoc response
     */
    public CsapDocResponse apidoc(String tableName, Boolean devTools) {
        return scannerApiPackage(devTools, tableName);
    }

    /**
     * 扫描包
     */
    public CsapDocResponse scannerApiPackage(Boolean devTools, String tableName) {
        List<CsapDocModelController> controllers = Lists.newArrayList();
        CsapDocResponse csapDocResponse = new CsapDocResponse();
        if (CollectionsUtil.isNotEmpty(packageConfig.getApiClasseList())) {
            for (Class<?> cl : packageConfig.getApiClasseList()) {
                if (StringUtil.isNotEmpty(tableName) && !filterTable(cl, tableName)) {
                    continue;
                }
                if (devTools && devtoolsClass.devtools()) {
                    cl = devtoolsClass.toolsClass(cl);
                }
                if (!cl.isAnnotationPresent(RestController.class) && !cl.isAnnotationPresent(Controller.class)) {
                    continue;
                }
                Api api = cl.getAnnotation(Api.class);
                boolean ab = api == null;
                if (ab) {
                    continue;
                }
                if (api.hidden() && (!devTools || !devtoolsClass.devtools())) {
                    continue;
                }
                RequestMapping requestMapping = cl.getAnnotation(RequestMapping.class);
                CsapDocModelController csapDocController = CsapDocModelController.builder()
                        .description(ab ? cl.getSimpleName() : api.description())
                        .methodList(new ArrayList<>())
                        .hidden(api.hidden())
                        .devTools(devTools)
                        .name(cl.getName())
                        .path(requestMapping == null || CollectionsUtil.isEmpty(requestMapping.value()) ? null : requestMapping.value())
                        .position(ab ? 1 : api.position())
                        .protocols(ab ? Protocols.HTTP : api.protocols())
                        .hiddenMethod(api == null ? null : Arrays.asList(api.hiddenMethod()))
                        .group(ab ? group : set(api.group()))
                        .version(ab ? version : set(api.version()))
                        .status(ab ? ApiStatus.DEFAULT : api.status())
                        .tags(ab ? null : api.tags())
                        .search(new StringBuilder())
                        .value(ab ? cl.getSimpleName() : api.value())
                        .build();
                controllers.add(csapDocController);
                csapDocController.getSearch().append(csapDocController.getValue()).append(csapDocController.getDescription());
                if (CollectionsUtil.isNotEmpty(csapDocController.getTags())) {
                    csapDocController.getSearch().append(CollectionsUtil.toString(Arrays.asList(csapDocController.getTags())));
                }
                csapDocResponse.getVersions().addAll(csapDocController.getVersion());
                getMethods(csapDocController, cl.getMethods(), new TypeVariableModel(cl));
                csapDocResponse.getGroups().addAll(csapDocController.getGroup());
            }
        }
        return scnnerResultEnumPackage(csapDocResponse
                .setApiInfo(packageConfig.getConfig().getApiInfo())
                .setResources(resourcesProvider.get())
                .setGlobalApiList(globalApiList)
                .setApiList(controllers.stream()
                        .sorted(Comparator.comparingInt(CsapDocModelController::getPosition)).collect(Collectors.toList())));
    }

    /**
     * 获取返回的枚举
     *
     * @param csapDocResponse 返回信息
     * @return apidocResponse
     */
    public CsapDocResponse scnnerResultEnumPackage(CsapDocResponse csapDocResponse) {
        List<CsapDocEnum> list = null;
        if (CollectionsUtil.isNotEmpty(packageConfig.getEnumClasseList())) {
            list = Lists.newArrayList();
            for (Class<?> cl : packageConfig.getEnumClasseList()) {
                if (!cl.isAnnotationPresent(ApiResponseCode.class)) {
                    continue;
                }
                if (!cl.isEnum()) {
                    continue;
                }
                List<CsapDocEnum.CsapDocEnumCode> enumCodes = Lists.newArrayList();
                list.add(new CsapDocEnum(cl.getAnnotation(ApiResponseCode.class).value(), enumCodes));
                boolean isIntface = false;
                for (Class<?> cll : cl.getInterfaces()) {
                    if (cll.getName().equals(BaseResultCode.class.getName())) {
                        continue;
                    }
                    isIntface = true;
                }
                if (!isIntface) {
                    continue;
                }
                BaseResultCode<?, ?>[] bcs = (BaseResultCode<?, ?>[]) cl.getEnumConstants();
                if (CollectionsUtil.isEmpty(bcs)) {
                    continue;
                }
                for (BaseResultCode<?, ?> code : bcs) {
                    enumCodes.add(new CsapDocEnum.CsapDocEnumCode(code.getName(), code.getCode() instanceof String ? (String) code.getCode() : code.getCode().toString(), code.getMessage()));
                }
            }
        }
        csapDocResponse.setDocEnumList(list);
        return csapDocResponse;

    }

    /**
     * 处理所有方法
     *
     * @param docController     controller
     * @param methods           methos
     * @param typeVariableModel 父类的类型参数
     */
    public void getMethods(CsapDocModelController docController, Method[] methods, TypeVariableModel typeVariableModel) {
        for (Method method : methods) {
            Boolean hidden = Boolean.FALSE;
            if (CollectionsUtil.isNotEmpty(docController.getHiddenMethod()) && docController.getHiddenMethod().contains(method.getName())) {
                if ((!devtoolsClass.devtools() || !docController.getDevTools())) {
                    continue;
                }
                hidden = Boolean.TRUE;
            }
            if (method.isBridge()) {
                continue;
            }
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            boolean ab = apiOperation == null;
            if (ab) {
                continue;
            }
            if (apiOperation.hidden()) {
                if ((!devtoolsClass.devtools() || !docController.getDevTools())) {
                    continue;
                } else {
                    hidden = Boolean.TRUE;
                }
            }
            CsapDocMethod docMethod = forMapping(method, CsapDocMethod.builder()
                    .devTools(docController.getDevTools())
                    .hidden(hidden)
                    .description(ab ? null : apiOperation.description())
                    .key(docController.getName() + StringPool.DOT + method.getName())
                    .name(method.getName())
                    .request(Lists.newArrayList())
                    .response(Lists.newArrayList())
                    .methods(Lists.newArrayList())
                    .apiPath(docController.getPath())
                    .group(ab || group.containsAll(Arrays.asList(apiOperation.group())) ? group : set(apiOperation.group(), docController.getGroup()))
                    .version(ab || group.containsAll(Arrays.asList(apiOperation.version())) ? version : set(apiOperation.version(), docController.getVersion()))
                    .tags(ab ? null : apiOperation.tags())
                    .search(new StringBuilder())
                    .value(ab ? null : apiOperation.value())
                    .status(ab ? ApiStatus.DEFAULT : apiOperation.status())
                    .paramTypes(Sets.newHashSet())
                    .paramType(apiOperation.paramType())
                    .methodHeaders(Lists.newArrayList())
                    .build());
            if (CollectionsUtil.isEmpty(docMethod.getMethods())) {
                continue;
            }
            if (containsPathMethod(docController, docMethod)) {
                continue;
            }
            headers(apiOperation, docMethod);
            docController.getMethodList().add(docMethod);
            docController.getGroup().addAll(docMethod.getGroup());
            docController.getVersion().addAll(docMethod.getVersion());
            docMethod.getSearch().append(docMethod.getValue()).append(docMethod.getDescription());
            if (CollectionsUtil.isNotEmpty(docMethod.getTags())) {
                docMethod.getSearch().append(CollectionsUtil.toString(Arrays.asList(docMethod.getTags())));
            }
            docController.getSearch().append(docMethod.getSearch());
            if (CollectionsUtil.isNotEmpty(model(docMethod, method.getGenericParameterTypes(), method, true, typeVariableModel)) && !globalRequestParam.isEmpty()) {
                if (docMethod.getRequest().get(0).getModelType().equals(ModelType.OBJECT)) {
                    List<String> list = globalRequestParam.keySet().stream().filter(str -> docMethod.getName().contains(str)).collect(Collectors.toList());
                    if (CollectionsUtil.isNotEmpty(list)) {
                        docMethod.addRequest(BeanCopierUtil.copyList(globalRequestParam.get(list.get(0)), CsapDocModel.class));
                        docMethod.getParamTypes().addAll(globalRequestParam.get(list.get(0)).stream().map(CsapDocModel::getParameters).findAny().get().stream().map(i -> i.getParamType().name()).collect(Collectors.toList()));
                    }
                }
            }
            methodReturnType(method, docMethod, typeVariableModel);
            if (CollectionsUtil.isEmpty(docMethod.getParamTypes())) {
                docMethod.getParamTypes().add(docMethod.getParamType().name());
            }
        }
        docController.setMethodList(docController.getMethodList().stream().sorted(Comparator.comparing(CsapDocMethod::getName)).collect(Collectors.toList()));

    }

    /**
     * 查找是否有相同的方法
     *
     * @param docController controller
     * @param docMethod     method
     * @return true/false
     */
    public boolean containsPathMethod(CsapDocModelController docController, CsapDocMethod docMethod) {
        List<String> paths = Arrays.asList(docMethod.getPaths());
        if (CollectionsUtil.isEmpty(paths)) {
            return false;
        }
        return docController.getMethodList().stream().anyMatch(i -> {
            if (Arrays.asList(i.getPaths()).stream().anyMatch(ii -> paths.stream().anyMatch(xi -> ii.equals(xi)))) {
                if (docMethod.getMethods().stream().anyMatch(x -> i.getMethods().stream().anyMatch(xx -> xx.equals(x)))) {
                    return true;
                }
            }
            return false;
        });
    }

    /**
     * 头部文件
     *
     * @param apiOperation
     * @param method
     */
    public void headers(ApiOperation apiOperation, CsapDocMethod method) {
        if (apiOperation == null) {
            return;
        }
        if (CollectionsUtil.isEmpty(apiOperation.headers())) {
            return;
        }
        Arrays.asList(apiOperation.headers()).forEach(headers -> method.getMethodHeaders().add(CsapDocMethodHeaders.builder()
                .key(headers.key())
                .value(headers.value())
                .description(headers.description())
                .example(headers.example())
                .hidden(headers.hidden())
                .position(headers.position())
                .required(headers.required())
                .build()));
    }

    /**
     * 处理mapping映射
     *
     * @param method
     * @param docMethod
     * @return
     */
    public CsapDocMethod forMapping(Method method, CsapDocMethod docMethod) {
        if (method.isAnnotationPresent(RequestMapping.class)) {
            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
            docMethod.setPaths(requestMapping.value());
            if (CollectionsUtil.isEmpty(requestMapping.method())) {
                docMethod.getMethods().addAll(Arrays.asList(RequestMethod.POST, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.GET));
            } else {
                docMethod.getMethods().addAll(Arrays.asList(requestMapping.method()));
            }
        } else if (method.isAnnotationPresent(PostMapping.class)) {
            PostMapping postMapping = method.getAnnotation(PostMapping.class);
            docMethod.setPaths(postMapping.value());
            docMethod.getMethods().add(RequestMethod.POST);
        } else if (method.isAnnotationPresent(DeleteMapping.class)) {
            DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
            docMethod.setPaths(deleteMapping.value());
            docMethod.getMethods().add(RequestMethod.DELETE);
        } else if (method.isAnnotationPresent(PutMapping.class)) {
            PutMapping putMapping = method.getAnnotation(PutMapping.class);
            docMethod.setPaths(putMapping.value());
            docMethod.getMethods().add(RequestMethod.PUT);
        } else if (method.isAnnotationPresent(GetMapping.class)) {
            GetMapping getMapping = method.getAnnotation(GetMapping.class);
            docMethod.setPaths(getMapping.value());
            docMethod.getMethods().add(RequestMethod.GET);
        } else if (method.isAnnotationPresent(PatchMapping.class)) {
            PatchMapping getMapping = method.getAnnotation(PatchMapping.class);
            docMethod.setPaths(getMapping.value());
            docMethod.getMethods().add(RequestMethod.PATCH);
        }
        return docMethod;
    }

    /**
     * 处理返回参数
     *
     * @param method            原始方法
     * @param docMethod         文档方法
     * @param typeVariableModel 类型模型
     */
    public void methodReturnType(Method method, CsapDocMethod docMethod, TypeVariableModel typeVariableModel) {
        model(BaseModel.build().key(docMethod.getKey()), docMethod, new Type[]{method.getGenericReturnType()}, method, false, typeVariableModel, true, true);
    }

    /**
     * model处理方法
     *
     * @param docMethod 文档方法
     * @param classes   需要处理的class数组
     * @param method    原始方法
     * @param request   是否请求参数
     * @return 返回列表实体类
     */
    public List<CsapDocModel> model(CsapDocMethod docMethod, Type[] classes, Method method, boolean request) {
        return model(BaseModel.build().key(docMethod.getKey()), docMethod, classes, method, request, null, true, true);
    }

    public List<CsapDocModel> model(CsapDocMethod docMethod, Type[] classes, Method method, boolean request, TypeVariableModel typeVariableModel) {
        return model(BaseModel.build().key(docMethod.getKey()), docMethod, classes, method, request, typeVariableModel, true, true);
    }

    /**
     * 处理方法参数
     *
     * @param docMethod 方法对象
     * @param classes   方法 参数
     * @param method    方法对象
     * @param add       是否添加 model
     * @return
     */
    public List<CsapDocModel> model(BaseModel baseModel, CsapDocMethod docMethod, Type[] classes, Method method, boolean request, TypeVariableModel typeVariableModel, boolean next, boolean add) {
        if (CollectionsUtil.isEmpty(classes)) {
            return null;
        }
        List<CsapDocModel> list = Lists.newArrayList();
        for (int x = 0; x < classes.length; x++) {
            Type t = classes[x];
            if (t.getTypeName().equalsIgnoreCase("void")) {
                continue;
            }
            ModelTypepProperties typepProperties = ClazzUtils.getRawClassType(t, typeVariableModel);
            Class cl = typepProperties.getModelType().equals(ModelType.T_OBJECT) ? typepProperties.getRawCl() : typepProperties.getCl();
            if (cl == null) {
                continue;
            }
            if (FinalConfigParam.validateExcludMap.contains(typepProperties.getRawType()) || FinalConfigParam.obj.contains(typepProperties.getRawType())) {
                continue;
            }
            ApiModel apiModel = (ApiModel) cl.getAnnotation(ApiModel.class);
            boolean n = apiModel == null;
            CsapDocModel docModel;
            boolean params = false;
            if ((FinalConfigParam.DATA_TYPE.contains(cl.getName()) || FinalConfigParam.OTHER_DATA_TYPE.contains(cl.getName()) || Enum.class.isAssignableFrom(cl))) {
                //基础数据类型
                params = true;
                if (request) {
                    Parameter[] parameters = method.getParameters();
                    String[] strings = parameterNameDiscoverer.getParameterNames(method);
                    docModel = methodBasicParams(typepProperties, docMethod, strings[x], parameters[x], method);
                    docMethod.getParamTypes().add(docModel.getParameters().get(0).getParamType().name());
                } else {
                    docModel = methodReturnBasicType(typepProperties, docMethod);
                }
                //TODO 特殊类型处理--如枚举
                if (Enum.class.isAssignableFrom(cl)) {
                    //TODO 特殊类型处理--如枚举
                    docModel.getParameters().get(0).setExtendDescr(enumData(cl));
                }
            } else {
                //对象类型
                docModel = CsapDocModel.builder()
                        .description(n ? "" : apiModel.description())
                        .name(cl.getName())
                        .value(n ? cl.getName() : apiModel.value())
                        .modelType(typepProperties.getModelType())
                        .parameters(new ArrayList<>())
                        .group(n ? group : set(apiModel.group(), docMethod.getGroup()))
                        .version(n ? version : set(apiModel.version(), docMethod.getVersion()))
                        .build();
            }
            if (add) {
                List<CsapDocModel> docModels = request ? docMethod.getRequest() : docMethod.getResponse();
                docModels.add(docModel);
            }
            list.add(docModel);
            if (params) {
                continue;
            }
            if (!FinalConfigParam.obj.contains(cl.getName())) {
                if (CollectionsUtil.isEmpty(baseModel.getTypes()) && CollectionsUtil.isNotEmpty(typepProperties.getClList())) {
                    baseModel.setTypes(typepProperties.getClList());
                } else if (cl.getGenericSuperclass() instanceof ParameterizedType) {
                    baseModel.setTypes(Arrays.asList(((ParameterizedType) cl.getGenericSuperclass()).getActualTypeArguments()));
                }
                getModelField(baseModel.modelClass(cl).appendKeyName(cl.getSimpleName()), ClazzUtils.getAllFields(cl), docMethod, docModel, method, request, typeVariableModel, next);
            }
        }
        return list;
    }


    /**
     * 方法的单参数 基础数据类型
     *
     * @param typepProperties
     * @return
     */
    public CsapDocModel methodBasicParams(ModelTypepProperties typepProperties, CsapDocMethod docMethod, String paramName, Parameter parameter, Method method) {
        ApiProperty property = apiPropertys(method, parameter.getName());
        CsapDocModel csapDocModel = CsapDocModel.builder()
                .modelType(ModelType.BASE_DATA)
                .name(typepProperties.getCl().getName())
                .parameters(new ArrayList<>())
                .build();
        if (property != null && property.hidden()) {
            if ((!docMethod.getDevTools() || !devtoolsClass.devtools())) {
                return csapDocModel;
            }
        }
        if (property != null && property.required()) {
            validateFactory.addRequestValidate(docMethod.getKey(), parameter.getType().getSimpleName(), parameter.getAnnotations(), paramName, BaseModel
                    .build()
                    .modelType(ModelType.BASE_DATA)
                    .field(parameter.getType()));
        }
        CsapDocParameter csapDocParameter = CsapDocParameter.builder()
                .dataType(typepProperties.getSubStr())
                .hidden(property==null?Boolean.FALSE:property.hidden())
                .longDataType(parameter.getType().getName())
                .defaultValue(property == null ? "" : property.defaultValue())
                .description(property == null ? "" : property.description())
                .example(property == null ? "" : property.example())
                .name(paramName)
                .group(property == null ? group : set(property.group(), docMethod.getGroup()))
                .version(property == null ? version : set(property.version(), docMethod.getVersion()))
                .position(property == null ? 0 : property.position())
                .length(property == null ? 0 : property.length())
                .decimals(property == null ? 0 : property.decimals())
                .value(property == null ? "" : property.value())
                .paramType(property == null ? ParamType.QUERY : property.paramType())
                .required(property == null ? false : property.required())
                .build();
        csapDocParameter.setKey(getKey(csapDocParameter));
        csapDocModel.getParameters().add(csapDocParameter);
        return csapDocModel;
    }


    /**
     * 获取方法可用字段
     *
     * @param fields    字段列表
     * @param docMethod 方法对象
     * @param docModel  model对象
     * @param method    方法
     * @param request   是否request参数
     */
    public void getModelField(BaseModel types, List<Field> fields, CsapDocMethod docMethod, CsapDocModel docModel, Method method, boolean request, TypeVariableModel typeVariableModel, boolean next) {
        for (Field field : fields) {
            ApiModelProperty property = field.getAnnotation(ApiModelProperty.class);
            if (property == null) {
                continue;
            }
            if ((property.hidden())) {
                if ((!docMethod.getDevTools() || !devtoolsClass.devtools())) {
                    continue;
                }
            }
            if (request ? property.ignoreReq() : property.ignoreRep()) {
                continue;
            }
            boolean filterField = (request) ?
                    (packageConfig.getConfig().getPageConfig().containsPageMethod(method.getName()) &&
                            packageConfig.getConfig().getPageConfig().getPageField().contains(field.getName())
                    ) :
                    (packageConfig.getConfig().getPageConfig().containsResultPageMethod(method.getName()) &&
                            packageConfig.getConfig().getPageConfig().getResultField().contains(field.getName())
                            && !packageConfig.getConfig().getPageConfig().getResultExcludes().contains(field.getName())
                    );
            if (filterField || (request ? property.forceReq() : property.forceRep())) {
                group(types, field, docMethod, docModel, method, request, typeVariableModel, property, null, next);
            } else {
                if (CollectionsUtil.isNotEmpty(property.groups())) {
                    //特殊过滤字段
                    for (Group group : property.groups()) {
                        if (!isEq(group, method, typeVariableModel)) {
                            continue;
                        }
                        group(types, field, docMethod, docModel, method, request, typeVariableModel, property, group, next);
                    }
                }
            }
        }
        docModel.setParameters(docModel.getParameters().stream().sorted(Comparator.comparing(CsapDocParameter::getName)).collect(Collectors.toList()));
    }

    public static boolean isEq(Group group, Method method, TypeVariableModel typeVariableModel) {
        //是否有点--标识带有类名
        if (group.value().contains(StringPool.DOT)) {
            return group.value().equals(typeVariableModel.getAClass().getSimpleName() + StringPool.DOT + method.getName());
        } else {
            return group.value().equals(method.getName());
        }
    }

    /**
     * 处理 group
     *
     * @param field             字段信息
     * @param docMethod         方法
     * @param docModel          文档模型
     * @param method            方法具体实例
     * @param request           是否请求参数
     * @param typeVariableModel 泛型使用<带有真实对象>
     * @param property          属性
     * @param group             分组
     * @param next              是否执行下一步
     */
    private void group(BaseModel types, Field field, CsapDocMethod docMethod, CsapDocModel docModel, Method method, boolean request, TypeVariableModel typeVariableModel, ApiModelProperty property, Group group, boolean next) {
        boolean exits = false;

        CsapDocParameter docParameter = CsapDocParameter.builder()
                .defaultValue(property.defaultValue())
                .description(property.description())
                .hidden(property.hidden())
                .example(property.example())
                .name(StringUtil.isEmpty(property.name()) ? field.getName() : property.name())
                .position(property.position())
                .value(property.value())
                .paramType(group == null ? property.paramType() : group.request().paramType())
                .required(group == null ? property.required() : group.request().required())
                .group(CollectionsUtil.isEmpty(property.group()) ? this.group : set(property.group(), docModel.getGroup()))
                .version(CollectionsUtil.isEmpty(property.version()) ? this.version : set(property.version(), docModel.getVersion()))
                .length(property.length())
                .decimals(property.decimals())
                .extendDescr(enumData(field.getType()))
                .build();
        if (docMethod.getParamType().compareTo(ParamType.DEFAULT) != 0) {
            docParameter.setParamType(docMethod.getParamType());
        }
        docParameter.setKey(getKey(docParameter));
        ModelTypepProperties fileType = getFildType(types.getTypes(), field, typeVariableModel);
        docParameter.setDataType(fileType.getSubStr());
        docParameter.setModelType(fileType.getModelType());
        if (!request) {
            if (group == null || group.response().include()) {
                exits = true;
                docModel.getParameters().add(docParameter);
                validateFactory.addIncluds(docMethod.getKey(), field.getName(), types.getModelClass());
            }
            if (group != null) {
                docParameter.setRequired(group.response().required());
            } else if (property != null) {
                docParameter.setRequired(property.required());
            }
        } else {
            if (group == null || group.request().value()) {
                exits = true;
                docModel.getParameters().add(docParameter);
                docMethod.getParamTypes().add(docParameter.getParamType().name());
            }
        }
        Validate.ValidateField validateField = null;
        if (request && exits && docParameter.isRequired()) {
            validateField = validateFactory.addRequestValidate(docMethod.getKey(), types.keyName(types.getModelClass().getSimpleName()).toStringKeyName(), field.getAnnotations(), field.getName(), types.field(field.getType()).modelType(docModel.getModelType()));
        }
        //处理对象使用
        if (exits && (!FinalConfigParam.DATA_TYPE.contains(fileType.getRawType()))) {
            if (Enum.class.isAssignableFrom(fileType.getRawCl())) {
                //TODO 特殊类型处理--如枚举
                docParameter.setExtendDescr(enumData(fileType.getRawCl()));
                return;
            }
            if (FinalConfigParam.list.contains(fileType.getRawType())) {
                Type[] type = ((ParameterizedType) fileType.getType()).getActualTypeArguments();
                if (CollectionsUtil.isNotEmpty(type)) {
                    Class[] cl = new Class[]{fileType.getCl()};
                    if (!next) {
                        return;
                    }
                    if (FinalConfigParam.DATA_TYPE.contains(cl[0].getName()) || FinalConfigParam.OTHER_DATA_TYPE.contains(cl[0].getName())) {
                        //处理基本数据类
                        return;
                    }
                    if (cl[0].getName().equals(docModel.getName())) {
                        next = false;
                    }
                    List<CsapDocModel> list = model(types
                            .clone()
                            .appendKeyName(field.getName())
                            .modelType(fileType.getModelType())
                            .validateField(validateField == null ? null : validateField.getChildren())
                            .types(fileType.getClList()), docMethod, cl, method, request, typeVariableModel, next, false);
                    if (CollectionsUtil.isNotEmpty(list)) {
                        list.get(0).setModelType(ModelType.ARRAY);
                        docParameter.setChildren(list.get(0));
                    }
                }
            } else {
                if (!next) {
                    return;
                }
                if (fileType.getRawType().equals(docModel.getName())) {
                    next = false;
                }
                if (FinalConfigParam.obj.contains(fileType.getRawType())) {
                    return;
                }
                List<CsapDocModel> list = model(types
                                .clone()
                                .appendKeyName(field.getName())
                                .modelType(fileType.getModelType())
                                .types(fileType.getClList())
                                .validateField(validateField == null ? null : validateField.getChildren()),
                        docMethod, new Type[]{fileType.getModelType().equals(ModelType.T_OBJECT) ? fileType.getRawCl() : fileType.getCl()},
                        method, request, typeVariableModel, next, false);
                if (CollectionsUtil.isNotEmpty(list)) {
                    docParameter.setChildren(list.get(0));
                }
            }
        }
    }


}
